/*
 * Copyright 2011 J. Auer, J. Andert, F.-J. Jendrusch, U. Celebi
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.fuberlin.replication.test;

import java.util.Iterator;
import java.util.Vector;

import junit.framework.Assert;
import android.test.AndroidTestCase;
import de.fuberlin.replication.library.Metadata;
import de.fuberlin.replication.library.Subset;
import de.fuberlin.replication.library.Version;
import de.fuberlin.replication.library.VersionVector;
import de.fuberlin.replication.test.communication.ConnectionDummy;
import de.fuberlin.replication.test.library.ApplicationDummy;
import de.fuberlin.replication.test.library.MetadataDAODummy;
import de.fuberlin.replication.test.library.StorageDummy;
import de.fuberlin.replication.test.library.SubsetDAODummy;
import de.fuberlin.replication.test.library.VectorSetsProxy;

/**
 * @author JohnAndert
 *
 */
public class VectorSetsMethodTesting extends AndroidTestCase {

	private VectorSetsProxy vectorSets;
	private ApplicationDummy appA ;
	
	/* (non-Javadoc)
	 * @see junit.framework.TestCase#setUp()
	 */
	protected void setUp() throws Exception {
		super.setUp();
		
		appA = new ApplicationDummy();
		appA.address = "repA";


		StorageDummy stoA = new StorageDummy();
		
		appA.stor = stoA;
		appA.conn = new ConnectionDummy();
		stoA.subsetDAO = new SubsetDAODummy();

		stoA.metadataDAO = new MetadataDAODummy();
		
		vectorSets = new VectorSetsProxy(appA);
	}
	
	public void test_containsObject_ObjectInSubsetOneSubset(){
		String obj1 	= "obj1";
		String obj2 	= "obj2";
		Version init	= new Version("repA",1);
		Version first	= new Version("repA",2);
		VersionVector vv= new VersionVector();
		vv.add(init);
		vv.add(first);
		
		Subset newSet 		= appA.getStorage().getSubsetDAO().create();
		
		newSet.setKnowledge(vv);
		
		Metadata m1		= new Metadata(obj1,init);
		Metadata m2		= new Metadata(obj2,init);
		newSet.addObjectName(obj1);
		newSet.addObjectName(obj2);
		
		appA.getStorage().getMetadataDAO().save(newSet, m1);
		appA.getStorage().getMetadataDAO().save(newSet, m2);
		appA.getStorage().getSubsetDAO().save(newSet);
		
		Subset s = VectorSetsProxy.containsObject(appA.getStorage().getSubsetDAO(), appA.getStorage().getMetadataDAO(), obj1);
		Assert.assertEquals(newSet.getKnowledge(), s.getKnowledge());
	}
	
	public void test_containsObject_ObjectInSubsetTwoSubsets(){
		String obj1 	= "obj1";
		String obj2 	= "obj2";
		Version init	= new Version("repA",1);
		Version first	= new Version("repA",2);
		VersionVector vv= new VersionVector();
		vv.add(init);
		vv.add(first);
		
		Subset newSet 		= appA.getStorage().getSubsetDAO().create();
		Subset targetSubset	= appA.getStorage().getSubsetDAO().create();
		
		newSet.setKnowledge(vv);
		targetSubset.setKnowledge(vv);
		
		Metadata m1		= new Metadata(obj1,init);
		Metadata m2		= new Metadata(obj2,init);
		newSet.addObjectName(obj1);
		
		targetSubset.addObjectName(obj2);
		
		appA.getStorage().getMetadataDAO().save(newSet, m1);
		appA.getStorage().getMetadataDAO().save(targetSubset, m2);
		appA.getStorage().getSubsetDAO().save(newSet);
		appA.getStorage().getSubsetDAO().save(targetSubset);
		 
		
		Subset s = VectorSetsProxy.containsObject(appA.getStorage().getSubsetDAO(), appA.getStorage().getMetadataDAO(), obj1);
		Assert.assertEquals(targetSubset.getKnowledge(), s.getKnowledge());
	}
	
	public void test_containsObject_ObjectInSubsetNullReturn(){
		String obj1 	= "obj1";
		String obj2 	= "obj2";
		Version init	= new Version("repA",1);
		VersionVector vv= new VersionVector();
		vv.add(init);
		
		Subset newSet 		= appA.getStorage().getSubsetDAO().create();
		
		newSet.setKnowledge(vv);
		
		Metadata m1		= new Metadata(obj1,init);
		newSet.addObjectName(obj1);
		
		
		appA.getStorage().getMetadataDAO().save(newSet, m1);
		appA.getStorage().getSubsetDAO().save(newSet);
		 
		
		Subset s = VectorSetsProxy.containsObject(appA.getStorage().getSubsetDAO(), appA.getStorage().getMetadataDAO(), obj2);
		Assert.assertEquals(null, s);
	}
	
	public void test_containsObject_vectorSubsetArray_first() {
		// Create subsets
		Vector<Subset[]> pairs = new Vector<Subset[]>();
		
		Vector<String> on1 = new Vector<String>();
		on1.add("o1");
		on1.add("o2");
		Subset ss1 = new Subset();
		ss1.setObjectNames(on1);
		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 1));
		Subset ss2 = new Subset(vv2);
		pairs.add(new Subset[]{ss1, ss2});
		
		Vector<String> on3 = new Vector<String>();
		on3.add("o3");
		on3.add("o4");
		Subset ss3 = new Subset();
		ss3.setObjectNames(on3);
		VersionVector vv4 = new VersionVector();
		vv4.add(new Version("B", 2));
		Subset ss4 = new Subset(vv4);
		pairs.add(new Subset[]{ss3, ss4});
		
		// Assert
		Assert.assertEquals(vv2, vectorSets.containsObject(pairs, "o1").getKnowledge());
	}
	
	public void test_containsObject_vectorSubsetArray_last() {
		// Create subsets
		Vector<Subset[]> pairs = new Vector<Subset[]>();
		
		Vector<String> on1 = new Vector<String>();
		on1.add("o1");
		on1.add("o2");
		Subset ss1 = new Subset();
		ss1.setObjectNames(on1);
		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 1));
		Subset ss2 = new Subset(vv2);
		pairs.add(new Subset[]{ss1, ss2});
		
		Vector<String> on3 = new Vector<String>();
		on3.add("o3");
		on3.add("o4");
		Subset ss3 = new Subset();
		ss3.setObjectNames(on3);
		VersionVector vv4 = new VersionVector();
		vv4.add(new Version("B", 2));
		Subset ss4 = new Subset(vv4);
		pairs.add(new Subset[]{ss3, ss4});
		
		// Assert
		Assert.assertEquals(vv4, vectorSets.containsObject(pairs, "o4").getKnowledge());
	}
	
	public void test_containsObject_vectorSubsetArray_null() {
		// Create subsets
		Vector<Subset[]> pairs = new Vector<Subset[]>();
		
		Vector<String> on1 = new Vector<String>();
		on1.add("o1");
		on1.add("o2");
		Subset ss1 = new Subset();
		ss1.setObjectNames(on1);
		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 1));
		Subset ss2 = new Subset(vv2);
		pairs.add(new Subset[]{ss1, ss2});
		
		Vector<String> on3 = new Vector<String>();
		on3.add("o3");
		on3.add("o4");
		Subset ss3 = new Subset();
		ss3.setObjectNames(on3);
		VersionVector vv4 = new VersionVector();
		vv4.add(new Version("B", 2));
		Subset ss4 = new Subset(vv4);
		pairs.add(new Subset[]{ss3, ss4});
		
		// Assert
		Assert.assertEquals(null, vectorSets.containsObject(pairs, "o5"));
	}
	
	public void test_containsVersion_smallerAtFirst() {
		// Create subsets
		Vector<Subset> subsets = new Vector<Subset>();
		
		VersionVector vv1 = new VersionVector();
		vv1.add(new Version("A", 3));
		vv1.add(new Version("B", 1));
		Subset s1 = new Subset(vv1);
		s1.getObjectNames().add("o1");
		subsets.add(s1);

		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 1));
		vv2.add(new Version("B", 1));
		subsets.add(new Subset(vv2));
		
		VersionVector vv3 = new VersionVector();
		vv3.add(new Version("A", 1));
		vv3.add(new Version("B", 3));
		subsets.add(new Subset(vv3));
		
		// Assert
		Assert.assertTrue(VectorSetsProxy.containsVersion(subsets, new Metadata("o1", new Version("A", 2))));
	}
	
	public void test_containsVersion_equalAtLast() {
		// Create subsets
		Vector<Subset> subsets = new Vector<Subset>();
		
		VersionVector vv1 = new VersionVector();
		vv1.add(new Version("A", 3));
		vv1.add(new Version("B", 1));
		subsets.add(new Subset(vv1));

		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 1));
		vv2.add(new Version("B", 1));
		subsets.add(new Subset(vv2));
		
		VersionVector vv3 = new VersionVector();
		vv3.add(new Version("A", 1));
		vv3.add(new Version("B", 3));
		Subset s3 = new Subset(vv3);
		s3.getObjectNames().add("o1");
		subsets.add(s3);
		
		// Assert
		Assert.assertTrue(VectorSetsProxy.containsVersion(subsets, new Metadata("o1", new Version("B", 3))));
	}
	
	public void test_containsVersion_false() {
		// Create subsets
		Vector<Subset> subsets = new Vector<Subset>();
		
		VersionVector vv1 = new VersionVector();
		vv1.add(new Version("A", 3));
		vv1.add(new Version("B", 1));
		subsets.add(new Subset(vv1));

		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 1));
		vv2.add(new Version("B", 1));
		Subset s2 = new Subset(vv2);
		s2.getObjectNames().add("o1");
		subsets.add(s2);
		
		VersionVector vv3 = new VersionVector();
		vv3.add(new Version("A", 1));
		vv3.add(new Version("B", 3));
		subsets.add(new Subset(vv3));
		
		// Assert
		Assert.assertFalse(VectorSetsProxy.containsVersion(subsets, new Metadata("o1", new Version("A", 4))));
	}
	
	public void test_union() {
		// Create subsets
		VersionVector vv1 = new VersionVector();
		vv1.add(new Version("A", 1));
		vv1.add(new Version("B", 1));
		Subset ss1 = new Subset(vv1);
		appA.getStorage().getSubsetDAO().save(ss1);
		
		VersionVector vv2 = new VersionVector();
		vv2.add(new Version("A", 2));
		vv2.add(new Version("B", 2));
		Subset ss2 = new Subset(vv2);
		appA.getStorage().getSubsetDAO().save(ss2);
		
		VersionVector vv3 = new VersionVector();
		vv3.add(new Version("A", 2));
		vv3.add(new Version("B", 2));
		Subset ss3 = new Subset(vv3);
		appA.getStorage().getSubsetDAO().save(ss3);
		
		// Test
		vectorSets.union();
		
		// Assert
		Iterator<Subset> subsets = appA.getStorage().getSubsetDAO().getAll().iterator();
		Assert.assertEquals(ss1.getKnowledge(), subsets.next().getKnowledge());
		Assert.assertEquals(ss2.getKnowledge(), subsets.next().getKnowledge());
		Assert.assertTrue(!subsets.hasNext());
	}
	
	public void test_merge_replicaUnknown() {
		// Create localSet
		VersionVector lvv = new VersionVector();
		lvv.add(new Version("A", 1));
		Subset localSet = new Subset(lvv);
		appA.getStorage().getSubsetDAO().save(localSet);
		
		Vector<String> lon = new Vector<String>();
		lon.add("o1");
		appA.getStorage().getMetadataDAO().save(localSet, new Metadata("o1", new Version("A", 1)));
		localSet.setObjectNames(lon);
	
		// Create remoteSet
		VersionVector rvv = new VersionVector();
		rvv.add(new Version("A", 1));
		rvv.add(new Version("B", 2));
		Subset remoteSet = new Subset(rvv);
		
		Vector<String> ron = new Vector<String>();
		ron.add("o1");
		remoteSet.setObjectNames(ron);
		
		// Create remoteObject
		Metadata remoteObject = new Metadata("o1", new Version("B", 2));
		Object remoteData = "O1";
		
		// Run test
		vectorSets.merge(localSet, remoteSet, remoteObject, remoteData);
		
		// Create changed localSet as expected
		VersionVector evv = new VersionVector();
		evv.add(new Version("A", 1));
		evv.add(new Version("B", 2));
		Subset expectedLocalSet = new Subset(evv);
		
		Vector<String> eon = new Vector<String>();
		eon.add("o1");
		expectedLocalSet.setObjectNames(eon);
		
		// Assert
		Assert.assertEquals(expectedLocalSet.getKnowledge(), localSet.getKnowledge());
		Assert.assertEquals(eon, lon);
		Assert.assertEquals(remoteObject.getVersion(), appA.getStorage().getMetadataDAO().get("o1").getVersion());
	}
	
	public void test_merge_replicaKnownOldVersion() {
		// Create localSet
		VersionVector lvv = new VersionVector();
		lvv.add(new Version("A", 2));
		lvv.add(new Version("B", 1));
		Subset localSet = new Subset(lvv);
		appA.getStorage().getSubsetDAO().save(localSet);
		
		Vector<String> lon = new Vector<String>();
		lon.add("o1");
		appA.getStorage().getMetadataDAO().save(localSet, new Metadata("o1", new Version("A", 2)));
		localSet.setObjectNames(lon);
	
		// Create remoteSet
		VersionVector rvv = new VersionVector();
		rvv.add(new Version("A", 1));
		rvv.add(new Version("B", 1));
		Subset remoteSet = new Subset(rvv);
		
		Vector<String> ron = new Vector<String>();
		ron.add("o1");
		remoteSet.setObjectNames(ron);
		
		// Create remoteObject
		Metadata remoteObject = new Metadata("o1", new Version("B", 1));
		Object remoteData = "O1";
		
		// Run test
		vectorSets.merge(localSet, remoteSet, remoteObject, remoteData);
		
		// Create changed localSet as expected
		VersionVector evv = new VersionVector();
		evv.add(new Version("A", 2));
		evv.add(new Version("B", 1));
		Subset expectedLocalSet = new Subset(evv);
		
		Vector<String> eon = new Vector<String>();
		eon.add("o1");
		expectedLocalSet.setObjectNames(eon);
		
		// Assert
		Assert.assertEquals(expectedLocalSet.getKnowledge(), localSet.getKnowledge());
		Assert.assertEquals(eon, lon);
		Assert.assertEquals(new Version("A", 2), appA.getStorage().getMetadataDAO().get("o1").getVersion());
	}
	
	public void test_merge_replicaKnownConflictingVersion() {
		// Create localSet
		VersionVector lvv = new VersionVector();
		lvv.add(new Version("A", 2));
		lvv.add(new Version("B", 1));
		Subset localSet = new Subset(lvv);
		appA.getStorage().getSubsetDAO().save(localSet);
		
		Vector<String> lon = new Vector<String>();
		lon.add("o1");
		appA.getStorage().getMetadataDAO().save(localSet, new Metadata("o1", new Version("A", 2), 1));
		localSet.setObjectNames(lon);
	
		// Create remoteSet
		VersionVector rvv = new VersionVector();
		rvv.add(new Version("A", 1));
		rvv.add(new Version("B", 2));
		Subset remoteSet = new Subset(rvv);
		
		Vector<String> ron = new Vector<String>();
		ron.add("o1");
		remoteSet.setObjectNames(ron);
		
		// Create remoteObject
		Metadata remoteObject = new Metadata("o1", new Version("B", 2), 2);
		Object remoteData = "O1";
		
		// Run test
		vectorSets.merge(localSet, remoteSet, remoteObject, remoteData);
		
		// Create changed localSet as expected
		VersionVector evv = new VersionVector();
		evv.add(new Version("A", 2));
		evv.add(new Version("B", 2));
		Subset expectedLocalSet = new Subset(evv);
		
		Vector<String> eon = new Vector<String>();
		eon.add("o1");
		expectedLocalSet.setObjectNames(eon);
		
		// Assert
		Assert.assertEquals(expectedLocalSet.getKnowledge(), localSet.getKnowledge());
		Assert.assertEquals(eon, lon);
		Assert.assertEquals(remoteObject.getVersion(),appA.getStorage().getMetadataDAO().get("o1").getVersion());
	}
	
	/**
	 * TestCase for the Split Method when only one Object is stored.
	 * The Split must result into an empty Subset and the second with the Object
	 */
	public void test_splitWithOneObjectsResultsInEmptySet(){
		
		String obj1 	= "obj1";
		Version init	= new Version("repA",1);
		VersionVector vv= new VersionVector();
		vv.add(init);
		
		Subset newSet = appA.getStorage().getSubsetDAO().create();
		newSet.setKnowledge(vv);
		
		Metadata m1		= new Metadata(obj1,init);
		newSet.addObjectName(obj1);
		
		appA.getStorage().getMetadataDAO().save(newSet, m1);
		appA.getStorage().getSubsetDAO().save(newSet);
		 
		Subset[] subsets = vectorSets.split(newSet, m1);
		
		Assert.assertEquals(2, subsets.length);
		
		// first Subset must be Empty Second must contain the Object
		Assert.assertTrue(subsets[0].getObjectNames().isEmpty());
		Assert.assertEquals(subsets[1].getObjectNames().get(0), m1.getName());
	}
	
	/**
	 * TestCase for the Split Method when two Objects are stored and splittet by the Second.
	 * The Split must result into two Subset containing each one Object
	 */
	public void test_splitWithTwoObjectsOneInEachResultSubset(){
		
		String obj1 	= "obj1";
		String obj2		= "obj2";
		
		Version init	= new Version("repA",1);
		Version first	= new Version("repA",2);
		VersionVector vv= new VersionVector();
		vv.add(first);
		
		Subset newSet = appA.getStorage().getSubsetDAO().create();
		newSet.setKnowledge(vv);
		
		Metadata m1		= new Metadata(obj1,init);
		Metadata m2		= new Metadata(obj2,first);
		
		
		newSet.addObjectName(obj1);
		newSet.addObjectName(obj2);
		
		
		appA.getStorage().getMetadataDAO().save(newSet, m1);
		appA.getStorage().getMetadataDAO().save(newSet, m2);
		appA.getStorage().getSubsetDAO().save(newSet);
		 
		Subset[] subsets = vectorSets.split(newSet, m2);
		
		Assert.assertEquals(2, subsets.length);
		
		Assert.assertEquals(subsets[0].getObjectNames().get(0), m1.getName());
		Assert.assertEquals(subsets[1].getObjectNames().get(0), m2.getName());
	}
	
	
	/**
	 * TestCase for the Split Method when two Objects are stored and splittet by the Second.
	 * The Split must result into two Subset containing each one Object
	 */
	public void test_splitWithThreeObjectsInTheMiddle(){
		
		String obj1 	= "obj1";
		String obj2		= "obj2";
		String obj3		= "obj3";
		
		Version init	= new Version("repA",1);
		Version first	= new Version("repA",2);
		Version second	= new Version("repB",2);
		VersionVector vv= new VersionVector();
		vv.add(first);
		vv.add(second);
		
		Subset newSet = appA.getStorage().getSubsetDAO().create();
		newSet.setKnowledge(vv);
		
		Metadata m1		= new Metadata(obj1,init);
		Metadata m2		= new Metadata(obj2,first);
		Metadata m3		= new Metadata(obj3,second);
		
		newSet.addObjectName(obj1);
		newSet.addObjectName(obj2);
		newSet.addObjectName(obj3);
		
		appA.getStorage().getMetadataDAO().save(newSet, m1);
		appA.getStorage().getMetadataDAO().save(newSet, m2);
		appA.getStorage().getMetadataDAO().save(newSet, m3);
		appA.getStorage().getSubsetDAO().save(newSet);
		 
		Subset[] subsets = vectorSets.split(newSet, m2);
		
		Assert.assertEquals(2, subsets.length);
		
		Assert.assertEquals(subsets[0].getObjectNames().get(0), m1.getName());
		Assert.assertEquals(subsets[0].getObjectNames().get(1), m3.getName());
		Assert.assertEquals(subsets[1].getObjectNames().get(0), m2.getName());
	}
	
	public void test_subsetFromNewObject(){
		Subset firstS 	= new Subset();
		Version first	= new Version("repA",2);
		String newObject = "newObject";
		
		VersionVector vv = new VersionVector();
		vv.add(first);
		firstS.setKnowledge(vv);
		Vector<String> names = new Vector<String>();
		names.add(newObject);
		firstS.setObjectNames(names);
		
		Metadata m 		= new Metadata(newObject,first);
		
		vectorSets.subsetFromNewObject(firstS, m, newObject);
		Metadata mApp 	= appA.getStorage().getMetadataDAO().get(newObject);

		// Compare the stored Metada
		// the test of Metadata equalitiy fails to some reason
		Assert.assertEquals(mApp.getName()		,m.getName());
		Assert.assertEquals(mApp.getVersion()	,m.getVersion());
		Assert.assertEquals(mApp.getTimestamp()	,m.getTimestamp());
		
		// Compare of the stored Knowledge Vectors
		Assert.assertTrue(firstS.getKnowledge().equals( appA.getStorage().getSubsetDAO().getAll().iterator().next().getKnowledge()));
	}
	
	/* (non-Javadoc)
	 * @see junit.framework.TestCase#tearDown()
	 */
	protected void tearDown() throws Exception {
		super.tearDown();
	}
}